CITransitionSelectorSample
==========================

* :download:`Download example <PyObjCExample-CITransitionSelectorSample.zip>`

Image transitions using Core Image.


.. rst-class:: tabber

Sources
-------

.. rst-class:: tabbertab

TransitionSelectorView.py
.........................

.. sourcecode:: python

    import math
    
    import Cocoa
    import objc
    import Quartz
    
    TRANSITION_COUNT = 9
    
    
    class TransitionSelectorView(Cocoa.NSView):
        _sourceImage = objc.ivar()
        _targetImage = objc.ivar()
        _shadingImage = objc.ivar()
        _blankImage = objc.ivar()
        _maskImage = objc.ivar()
        transitions = objc.ivar()  # Array
    
        base = objc.ivar(type=objc._C_DBL)
    
        thumbnailWidth = objc.ivar(type=objc._C_FLT)
        thumbnailHeight = objc.ivar(type=objc._C_FLT)
        thumbnailGap = objc.ivar(type=objc._C_FLT)
    
        def awakeFromNib(self):
            self.thumbnailWidth = 340.0
            self.thumbnailHeight = 240.0
            self.thumbnailGap = 20.0
    
            url = Cocoa.NSURL.fileURLWithPath_(
                Cocoa.NSBundle.mainBundle().pathForResource_ofType_("Rose", "jpg")
            )
            self.setSourceImage_(Quartz.CIImage.imageWithContentsOfURL_(url))
    
            url = Cocoa.NSURL.fileURLWithPath_(
                Cocoa.NSBundle.mainBundle().pathForResource_ofType_("Frog", "jpg")
            )
            self.setTargetImage_(Quartz.CIImage.imageWithContentsOfURL_(url))
    
            timer = Cocoa.NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
                1.0 / 30.0, self, "timerFired:", None, True
            )
    
            self.base = Cocoa.NSDate.pyobjc_classMethods.timeIntervalSinceReferenceDate()
            Cocoa.NSRunLoop.currentRunLoop().addTimer_forMode_(
                timer, Cocoa.NSDefaultRunLoopMode
            )
            Cocoa.NSRunLoop.currentRunLoop().addTimer_forMode_(
                timer, Cocoa.NSEventTrackingRunLoopMode
            )
    
        def setSourceImage_(self, source):
            self._sourceImage = source
    
        def setTargetImage_(self, target):
            self._targetImage = target
    
        def shadingImage(self):
            if self._shadingImage is None:
                url = Cocoa.NSURL.fileURLWithPath_(
                    Cocoa.NSBundle.mainBundle().pathForResource_ofType_("Shading", "tiff")
                )
                self._shadingImage = Quartz.CIImage.alloc().initWithContentsOfURL_(url)
    
            return self._shadingImage
    
        def blankImage(self):
            if self._blankImage is None:
                url = Cocoa.NSURL.fileURLWithPath_(
                    Cocoa.NSBundle.mainBundle().pathForResource_ofType_("Blank", "jpg")
                )
                self._blankImage = Quartz.CIImage.alloc().initWithContentsOfURL_(url)
    
            return self._blankImage
    
        def maskImage(self):
            if self._maskImage is None:
                url = Cocoa.NSURL.fileURLWithPath_(
                    Cocoa.NSBundle.mainBundle().pathForResource_ofType_("Mask", "jpg")
                )
                self._maskImage = Quartz.CIImage.alloc().initWithContentsOfURL_(url)
    
            return self._maskImage
    
        def timerFired_(self, sender):
            self.setNeedsDisplay_(True)
    
        def imageForTransition_atTime_(self, transitionNumber, t):
            transition = self.transitions[transitionNumber]
    
            if t % 2.0 < 1.0:
                transition.setValue_forKey_(self._sourceImage, "inputImage")
                transition.setValue_forKey_(self._targetImage, "inputTargetImage")
            else:
                transition.setValue_forKey_(self._targetImage, "inputImage")
                transition.setValue_forKey_(self._sourceImage, "inputTargetImage")
    
            transition._.inputTime = t % 1.0
    
            crop = Quartz.CIFilter.filterWithName_("CICrop")
            crop._.inputImage = transition._.outputImage
            crop._.inputRectangle = Quartz.CIVector.vectorWithX_Y_Z_W_(
                0, 0, self.thumbnailWidth, self.thumbnailHeight
            )
            return crop._.outputImage
    
        def drawRect_(self, rectangle):
            thumbFrame = Quartz.CGRectMake(0, 0, self.thumbnailWidth, self.thumbnailHeight)
            t = 0.4 * (
                Cocoa.NSDate.pyobjc_classMethods.timeIntervalSinceReferenceDate()
                - self.base
            )
    
            context = Cocoa.NSGraphicsContext.currentContext().CIContext()
    
            if self.transitions is None:
                self.transitions = [None] * TRANSITION_COUNT
                self.setupTransitions()
    
            w = int(math.ceil(math.sqrt(TRANSITION_COUNT)))
            origin = Quartz.CGPoint()
    
            for i in range(TRANSITION_COUNT):
                origin.x = (i % w) * (self.thumbnailWidth + self.thumbnailGap)
                origin.y = (i / w) * (self.thumbnailHeight + self.thumbnailGap)
    
                if context is not None:
                    context.drawImage_atPoint_fromRect_(
                        self.imageForTransition_atTime_(i, t + 0.1 * i), origin, thumbFrame
                    )
    
        def setupTransitions(self):
            w = self.thumbnailWidth
            h = self.thumbnailHeight
    
            extent = Quartz.CIVector.vectorWithX_Y_Z_W_(0, 0, w, h)
    
            self.transitions[0] = Quartz.CIFilter.filterWithName_("CISwipeTransition")
            self.transitions[0]._.inputExtent = extent
            self.transitions[0]._.inputColor = (
                Quartz.CIColor.colorWithRed_green_blue_alpha_(0, 0, 0, 0)
            )
            self.transitions[0]._.inputAngle = 0.3 * math.pi
            self.transitions[0]._.inputWidth = 80.0
            self.transitions[0]._.inputOpacity = 0.0
    
            self.transitions[1] = Quartz.CIFilter.filterWithName_("CIDissolveTransition")
    
            self.transitions[2] = Quartz.CIFilter.filterWithName_(
                "CISwipeTransition"
            )  # dupe
            self.transitions[2]._.inputExtent = extent
            self.transitions[2]._.inputColor = (
                Quartz.CIColor.colorWithRed_green_blue_alpha_(0, 0, 0, 0)
            )
            self.transitions[2]._.inputAngle = math.pi
            self.transitions[2]._.inputWidth = 2.0
            self.transitions[2]._.inputOpacity = 0.2
    
            self.transitions[3] = Quartz.CIFilter.filterWithName_("CIModTransition")
            self.transitions[3]._.inputCenter = Quartz.CIVector.vectorWithX_Y_(
                0.5 * w, 0.5 * h
            )
            self.transitions[3]._.inputAngle = math.pi * 0.1
            self.transitions[3]._.inputRadius = 30.0
            self.transitions[3]._.inputCompression = 10.0
    
            self.transitions[4] = Quartz.CIFilter.filterWithName_("CIFlashTransition")
            self.transitions[4]._.inputExtent = extent
            self.transitions[4]._.inputCenter = Quartz.CIVector.vectorWithX_Y_(
                0.3 * w, 0.7 * h
            )
            self.transitions[4]._.inputColor = (
                Quartz.CIColor.colorWithRed_green_blue_alpha_(1, 0.8, 0.6, 1)
            )
            self.transitions[4]._.inputMaxStriationRadius = 2.5
            self.transitions[4]._.inputStriationStrength = 0.5
            self.transitions[4]._.inputStriationContrast = 1.37
            self.transitions[4]._.inputFadeThreshold = 0.85
    
            self.transitions[5] = Quartz.CIFilter.filterWithName_(
                "CIDisintegrateWithMaskTransition"
            )
            self.transitions[5]._.inputMaskImage = self.maskImage()
            self.transitions[5]._.inputShadowRadius = 10.0
            self.transitions[5]._.inputShadowDensity = 0.7
            self.transitions[5]._.inputShadowOffset = Quartz.CIVector.vectorWithX_Y_(
                0.0, -0.05 * h
            )
    
            self.transitions[6] = Quartz.CIFilter.filterWithName_("CIRippleTransition")
            self.transitions[6]._.inputExtent = extent
            self.transitions[6]._.inputShadingImage = self.shadingImage()
            self.transitions[6]._.inputCenter = Quartz.CIVector.vectorWithX_Y_(
                0.5 * w, 0.5 * h
            )
            self.transitions[6]._.inputWidth = 80.0
            self.transitions[6]._.inputScale = 30.0
    
            self.transitions[7] = Quartz.CIFilter.filterWithName_("CICopyMachineTransition")
            self.transitions[7]._.inputExtent = extent
            self.transitions[7]._.inputColor = (
                Quartz.CIColor.colorWithRed_green_blue_alpha_(0.6, 1, 0.8, 1)
            )
            self.transitions[7]._.inputAngle = 0
            self.transitions[7]._.inputWidth = 40
            self.transitions[7]._.inputOpacity = 1.0
    
            self.transitions[8] = Quartz.CIFilter.filterWithName_("CIPageCurlTransition")
            self.transitions[8]._.inputExtent = extent
            self.transitions[8]._.inputShadingImage = self.shadingImage()
            self.transitions[8]._.inputBacksideImage = self.blankImage()
            self.transitions[8]._.inputAngle = -0.2 * math.pi
            self.transitions[8]._.inputRadius = 70.0

.. rst-class:: tabbertab

main.py
.......

.. sourcecode:: python

    import TransitionSelectorView  # noqa: F401
    from PyObjCTools import AppHelper
    
    AppHelper.runEventLoop()

.. rst-class:: tabbertab

setup.py
........

.. sourcecode:: python

    """
    Script for building the example.
    
    Usage:
        python3 setup.py py2app
    """
    
    from setuptools import setup
    
    setup(
        name="CITransitionSelectorSample",
        app=["main.py"],
        data_files=[
            "English.lproj",
            "Blank.jpg",
            "Frog.jpg",
            "Mask.jpg",
            "Rose.jpg",
            "Shading.tiff",
        ],
        setup_requires=["py2app", "pyobjc-framework-Cocoa", "pyobjc-framework-Quartz"],
    )

